#region File Description
//-----------------------------------------------------------------------------
// OptionsMenuScreen.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion

#region Using Statements
using Microsoft.Xna.Framework;
using System.IO;
using Microsoft.Xna.Framework.Graphics;
using EasyStorage;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Input;
using EventFlowLibrary.Player;
using EventFlowLibrary.GameStructure;
using LevelComponents;
using Microsoft.Xna.Framework.GamerServices;
#endregion

namespace ScreenManagement
{
    /// <summary>
    /// The options screen is brought up over the top of the main menu
    /// screen, and gives the user a chance to configure the game
    /// in various hopefully useful ways.
    /// </summary>
    class OptionsMenuScreen : MenuScreen
    {
        #region Fields

        /// <summary>
        /// Menu entries
        /// </summary>
        MenuEntry musicLevelMenuEntry;
        MenuEntry soundLevelMenuEntry;
        MenuEntry invertedMenuEntry;
        MenuEntry cameraBobMenuEntry;
        MenuEntry save;
        MenuEntry dontSave;

        /// <summary>
        /// Textures for the sound bars
        /// </summary>
        Texture2D blackTexture, blueTexture;

        /// <summary>
        /// The base music volume
        /// </summary>
        float musicVolume = 0.3f;

        /// <summary>
        /// The base volume for sound effects
        /// </summary>
        float soundVolume = 0.5f;

        /// <summary>
        /// Whether the controller is inverted
        /// </summary>
        bool inverted = true;

        /// <summary>
        /// Whether the camera should bob or not
        /// </summary>
        bool cameraBobEnabled = true;

        /// <summary>
        /// Track to change camera when options are changed in game
        /// </summary>
        Camera camera;

        #endregion

        #region Initialization


        /// <summary>
        /// Constructor.
        /// </summary>
        public OptionsMenuScreen(Camera _camera)
            : base("Options")
        {
            camera = _camera;

            // Create our menu entries.
            musicLevelMenuEntry = new MenuEntry(string.Empty, 2);
            soundLevelMenuEntry = new MenuEntry(string.Empty, 2);
            invertedMenuEntry = new MenuEntry(string.Empty, 2);
            cameraBobMenuEntry = new MenuEntry(string.Empty, 2);
            save = new MenuEntry("- Save -");
            dontSave = new MenuEntry("- Don't Save -");

            SetMenuEntryText();

            // Hook up menu event handlers.
            musicLevelMenuEntry.Selected += MusicLevelMenuEntrySelected;
            soundLevelMenuEntry.Selected += SoundLevelMenuEntrySelected;
            invertedMenuEntry.Selected += InvertedMenuEntrySelected;
            cameraBobMenuEntry.Selected += CameraBobMenuEntrySelected;
            save.Selected += OnCancel;
            save.Selected += SaveSettings;
            dontSave.Selected += OnCancel;
            
            // Add entries to the menu.
            MenuEntries.Add(musicLevelMenuEntry);
            MenuEntries.Add(soundLevelMenuEntry);
            MenuEntries.Add(invertedMenuEntry);
            MenuEntries.Add(cameraBobMenuEntry);
            MenuEntries.Add(save);
            MenuEntries.Add(dontSave);
        }

        /// <summary>
        /// Load the content
        /// </summary>
        public override void LoadContent()
        {
            // Load the base
            base.LoadContent();

            // Load our textures
            ContentManager content = new ContentManager(ScreenManager.Game.Services, "Content");
            blueTexture = content.Load<Texture2D>("Textures/Screens/blue");
            blackTexture = content.Load<Texture2D>("Textures/Screens/black");

            // Load content variables
            this.ScreenManager.SaveDevice.SaveCompleted -= SaveDevice_SaveCompleted;
            this.ScreenManager.SaveDevice.SaveCompleted += new SaveCompletedEventHandler(SaveDevice_SaveCompleted);

            // Set values
            inverted = this.ScreenManager.gameSettings.Inverted;
            musicVolume = this.ScreenManager.gameSettings.MusicVolume;
            soundVolume = this.ScreenManager.gameSettings.SoundEffectVolume;
            cameraBobEnabled = this.ScreenManager.gameSettings.CameraBobEnabled;

            SetMenuEntryText();
        }


        /// <summary>
        /// Fills in the latest values for the options screen menu text.
        /// </summary>
        void SetMenuEntryText()
        {
            musicVolume = MathHelper.Clamp(musicVolume, 0, 1);
            soundVolume = MathHelper.Clamp(soundVolume, 0, 1);

            musicLevelMenuEntry.Text = "Music: ";
            soundLevelMenuEntry.Text = "Sound: ";
            invertedMenuEntry.Text = "Inverted: " + (inverted ? "On" : "Off");
            cameraBobMenuEntry.Text = "Camera Bob: " + (cameraBobEnabled ? "On" : "Off");
        }


        #endregion

        public override void Draw(GameTime gameTime)
        {
            if (!this.IsExiting)
            {
                // Draw the sound bars. They are a percentage of 400 px, with a 35 out of 41 px black border
                this.ScreenManager.SpriteBatch.Begin();
                this.ScreenManager.SpriteBatch.Draw(blackTexture, new Rectangle(290, (int)this.MenuEntries[0].Position.Y - (this.ScreenManager.FontLarge.LineSpacing / 2) - 6, 420, 41), new Color(128, 128, 128, 128));
                this.ScreenManager.SpriteBatch.Draw(blackTexture, new Rectangle(290, (int)this.MenuEntries[1].Position.Y - (this.ScreenManager.FontLarge.LineSpacing / 2), 420, 41), new Color(128, 128, 128, 128));
                this.ScreenManager.SpriteBatch.Draw(blueTexture, new Rectangle(300, (int)this.MenuEntries[0].Position.Y - (41 / 2) - 3, (int)((float)musicVolume * 400.0f), 35), new Color(128, 128, 128, 128));
                this.ScreenManager.SpriteBatch.Draw(blueTexture, new Rectangle(300, (int)this.MenuEntries[1].Position.Y - (41 / 2) + 3, (int)((float)soundVolume * 400.0f), 35), new Color(128, 128, 128, 128));
                this.ScreenManager.SpriteBatch.End();

                base.Draw(gameTime);
            }
        }

        #region Handle Input

        public override void HandleInput(GameTime _gameTime, InputState input)
        {
            base.HandleInput(_gameTime, input);

            if (input.IsNewKeyPress(Keys.Left, this.ControllingPlayer.Value) || 
                input.IsNewButtonPress(Buttons.DPadLeft, this.ControllingPlayer.Value) || 
                input.IsNewButtonPress(Buttons.LeftShoulder, this.ControllingPlayer.Value) ||
                input.IsNewButtonPress(Buttons.LeftThumbstickLeft, this.ControllingPlayer.Value) || 
                input.IsNewButtonPress(Buttons.LeftTrigger, this.ControllingPlayer.Value) ||
                input.IsNewButtonPress(Buttons.RightThumbstickLeft, this.ControllingPlayer.Value))
            {
                switch (this.SelectedEntry)
                {
                    case 0:
                        musicVolume -= 0.02f;

                        // Set here, but reset if we don't save when exiting screen
                        Microsoft.Xna.Framework.Media.MediaPlayer.Volume = musicVolume; 
                        break;
                    case 1:
                        soundVolume -= 0.02f; break;
                    case 2:
                        inverted = !inverted; break;
                    case 3:
                        cameraBobEnabled = !cameraBobEnabled; break;
                    default:
                        break;
                }
                SetMenuEntryText();
            }
            else if (input.IsNewKeyPress(Keys.Right, this.ControllingPlayer.Value) || 
                input.IsNewButtonPress(Buttons.DPadRight, this.ControllingPlayer.Value) || 
                input.IsNewButtonPress(Buttons.RightShoulder, this.ControllingPlayer.Value) ||
                input.IsNewButtonPress(Buttons.LeftThumbstickRight, this.ControllingPlayer.Value) || 
                input.IsNewButtonPress(Buttons.RightTrigger, this.ControllingPlayer.Value) ||
                input.IsNewButtonPress(Buttons.RightThumbstickRight, this.ControllingPlayer.Value))
            {
                switch (this.SelectedEntry)
                {
                    case 0:
                        musicVolume += 0.02f;

                        // Set here, but reset if we don't save when exiting screen
                        Microsoft.Xna.Framework.Media.MediaPlayer.Volume = musicVolume; 
                        break;
                    case 1:
                        soundVolume += 0.02f; break;
                    case 2:
                        inverted = !inverted; break;
                    case 3:
                        cameraBobEnabled = !cameraBobEnabled; break;
                    default:
                        break;
                }
                SetMenuEntryText();
            }
        }


        /// <summary>
        /// Event handler for when the Ungulate menu entry is selected.
        /// </summary>
        void MusicLevelMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            musicVolume += 0.02f;

            // Set here, but reset if we don't save when exiting screen
            Microsoft.Xna.Framework.Media.MediaPlayer.Volume = musicVolume;

            SetMenuEntryText();
        }


        /// <summary>
        /// Event handler for when the Language menu entry is selected.
        /// </summary>
        void SoundLevelMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            soundVolume += 0.02f;

            SetMenuEntryText();
        }


        /// <summary>
        /// Event handler for when the Frobnicate menu entry is selected.
        /// </summary>
        void InvertedMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            inverted = !inverted;

            SetMenuEntryText();
        }

        /// <summary>
        /// Event handler for camera bob
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void CameraBobMenuEntrySelected(object sender, PlayerIndexEventArgs e)
        {
            cameraBobEnabled = !cameraBobEnabled;

            SetMenuEntryText();
        }

        /// <summary>
        /// Save the settings.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void SaveSettings(object sender, PlayerIndexEventArgs e)
        {
            SignedInGamer gamer = Gamer.SignedInGamers[(PlayerIndex)this.ControllingPlayer];

            if (gamer != null)
            {
                if (!gamer.IsGuest)
                {
                }
                else
                {
                    //sign in and purchase message box
                    MessageBoxScreen signInBox = new MessageBoxScreen(new string[] { "Please sign in to a non-guest account", "to save the settings!" }, true, false, true, true);
                    signInBox.Cancelled += OnCancel;
                    signInBox.Accepted += OnCancel;
                    ScreenManager.AddScreen(signInBox, this.ControllingPlayer.Value);

                    return;
                }
            }
            else
            {
                //sign in and purchase message box
                MessageBoxScreen signInBox = new MessageBoxScreen(new string[] { "Please sign in to a non-guest account", "to save the settings!" }, true, false, true, true);
                signInBox.Cancelled += OnCancel;
                signInBox.Accepted += OnCancel;
                ScreenManager.AddScreen(signInBox, this.ControllingPlayer.Value);

                return;
            }

            if (camera != null)
            {
                camera.CameraBobEnabled = cameraBobEnabled;
                camera.Inverted = (inverted ? 1 : -1);
            }

            // make sure the device is ready
            try
            {
                if (this.ScreenManager.SaveDevice.IsReady)
                {

                    // Show box
                    MessageBoxScreen currentSaveMessageBox = new MessageBoxScreen(new string[] { "Saving, please wait..." }, false, false, false, false);
                    currentSaveMessageBox.Cancelled -= OnCancel;
                    currentSaveMessageBox.Accepted -= OnCancel;
                    ScreenManager.AddScreen(currentSaveMessageBox, this.ControllingPlayer.Value);

                    // save a file asynchronously. this will trigger IsBusy to return true
                    // for the duration of the save process.
                    this.ScreenManager.SaveDevice.SaveAsync("MurderForDinner_Settings", "_settings",
                        stream =>
                        {
                            try
                            {
                                using (BinaryWriter writer = new BinaryWriter(stream))
                                {
                                    writer.Write((double)musicVolume);      // Write music volume
                                    writer.Write((double)soundVolume);      // Write sound volume
                                    writer.Write(inverted);                 // Write inverted setting
                                    writer.Write(cameraBobEnabled);         // Write camera bob
                                }
                            }
                            catch
                            {
                                // Remove topmost screen ("Saving settings, please wait...")
                                ScreenManager.RemoveScreenTopmost();

                                MessageBoxScreen confirmExitMessageBox = new MessageBoxScreen(new string[] { "Could not save settings. Please retry." }, true, false, true, true);
                                confirmExitMessageBox.Accepted += OnCancel;
                                ScreenManager.AddScreen(confirmExitMessageBox, this.ControllingPlayer.Value);
                            }
                        });

                }
                else
                {
                    MessageBoxScreen warningMessageBox = new MessageBoxScreen(new string[] { "Save device is not ready. Please retry." }, true, false, true, true);
                    warningMessageBox.Accepted += OnCancel;
                    ScreenManager.AddScreen(warningMessageBox, this.ControllingPlayer.Value);
                }
            }
            catch
            {
                // Remove topmost screen ("Saving settings, please wait...")
                ScreenManager.RemoveScreenTopmost();

                MessageBoxScreen warningMessageBox = new MessageBoxScreen(new string[] { "Save device is not ready. Please retry." }, true, false, true, true);
                warningMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(warningMessageBox, this.ControllingPlayer.Value);
            }
        }

        protected override void OnCancel(PlayerIndex playerIndex)
        {
            // Reset to old volume
            Microsoft.Xna.Framework.Media.MediaPlayer.Volume = ScreenManager.gameSettings.MusicVolume;

            base.OnCancel(playerIndex);
        }

        /// <summary>
        /// When settings are saved, notify user of results.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="args"></param>
        void SaveDevice_SaveCompleted(object sender, FileActionCompletedEventArgs args)
        {
            this.ScreenManager.SaveDevice.SaveCompleted -= SaveDevice_SaveCompleted;

            // Remove topmost screen
            ScreenManager.RemoveScreenTopmost();

            if (args.Error == null)
            {
                // Saved successfully
                MessageBoxScreen warningMessageBox = new MessageBoxScreen(new string[] { "Settings saved!" }, true, false, true, true);
                warningMessageBox.Cancelled += OnCancel;
                warningMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(warningMessageBox, this.ControllingPlayer.Value);

                // Set screenmanager values
                this.ScreenManager.gameSettings.Inverted = inverted;
                this.ScreenManager.gameSettings.MusicVolume = musicVolume;
                this.ScreenManager.gameSettings.SoundEffectVolume = soundVolume;
                this.ScreenManager.gameSettings.CameraBobEnabled = cameraBobEnabled;
            }
            else
            {
                // Errored
                MessageBoxScreen warningMessageBox = new MessageBoxScreen(new string[] { "Error saving file. ", args.Error.Message }, true, false, true, true);
                warningMessageBox.Cancelled += OnCancel;
                warningMessageBox.Accepted += OnCancel;
                ScreenManager.AddScreen(warningMessageBox, this.ControllingPlayer.Value);
            }
        }

        #endregion
    }
}
